#include <sdk.h>
#include <wx/tokenzr.h>
#include "parserthread.h"
#include "parser.h"
#include "expression.h"

#define CC_PARSERTHREAD_DEBUG_OUTPUT 0


#define CC_PARSERTHREAD_TESTDESTROY 0

#if CC_PARSERTHREAD_TESTDESTROY
#define IS_ALIVE IsStillAlive(wxString(__PRETTY_FUNCTION__, wxConvUTF8))
#else
#define IS_ALIVE !TestDestroy()
#endif

const wxString g_UnnamedSymbol = _T( "__Unnamed" );
namespace ParserConsts {
  const wxString empty( _T( "" ) );
  const wxChar   null( _T( '\0' ) );
  const wxChar   eol_chr( _T( '\n' ) );
  const wxString space( _T( " " ) );
  const wxChar   space_chr( _T( ' ' ) );
  const wxChar   tab_chr( _T( '\t' ) );
  const wxString equals( _T( "=" ) );
  const wxChar   equals_chr( _T( '=' ) );
  const wxString hash( _T( "#" ) );
  const wxChar   hash_chr( _T( '#' ) );
  const wxString plus( _T( "+" ) );
  const wxChar   plus_chr( _T( '+' ) );
  const wxString dash( _T( "-" ) );
  const wxChar   dash_chr( _T( '-' ) );
  const wxString ptr( _T( "*" ) );
  const wxChar   ptr_chr( _T( '*' ) );
  const wxString ref( _T( "&" ) );
  const wxChar   ref_chr( _T( '&' ) );
  const wxString comma( _T( "," ) );
  const wxChar   comma_chr( _T( ',' ) );
  const wxString dot( _T( "." ) );
  const wxChar   dot_chr( _T( '.' ) );
  const wxString colon( _T( ":" ) );
  const wxChar colon_chr( _T( ':' ) );
  const wxString semicolon( _T( ";" ) );
  const wxChar   semicolon_chr( _T( ';' ) );
  const wxChar   opbracket_chr( _T( '(' ) );
  const wxChar   clbracket_chr( _T( ')' ) );
  const wxString opbracket( _T( "(" ) );
  const wxString clbracket( _T( ")" ) );
  const wxString opbrace( _T( "{" ) );
  const wxChar   opbrace_chr( _T( '{' ) );
  const wxString clbrace( _T( "}" ) );
  const wxChar   clbrace_chr( _T( '}' ) );
  const wxString oparray( _T( "[" ) );
  const wxChar   oparray_chr( _T( '[' ) );
  const wxString clarray( _T( "]" ) );
  const wxChar   clarray_chr( _T( ']' ) );
  const wxString tilde( _T( "~" ) );
  const wxString lt( _T( "<" ) );
  const wxChar   lt_chr( _T( '<' ) );
  const wxString gt( _T( ">" ) );
  const wxChar   gt_chr( _T( '>' ) );
  const wxChar   underscore_chr( _T( '_' ) );
  const wxChar   question_chr( _T( '?' ) );
  const wxString dcolon( _T( "::" ) );
  const wxString opbracesemicolon( _T( "{;" ) );
  const wxString commaclbrace( _T( ",}" ) );
  const wxString semicolonopbrace( _T( ";{" ) );
  const wxString semicolonclbrace( _T( ";}" ) );
  const wxString gtsemicolon( _T( ">;" ) );
  const wxString quot( _T( "\"" ) );
  const wxString kw_do( _T( "do" ) );
  const wxString kw_if( _T( "if" ) );
  const wxString spaced_colon( _T( " : " ) );
  const wxString kw__C_( _T( "\"C\"" ) );
  const wxString kw_for( _T( "for" ) );
  const wxString kw_try( _T( "try" ) );
  const wxString commasemicolonopbrace( _T( ",;{" ) );
  const wxString kw___at( _T( "__at" ) );
  const wxString kw_else( _T( "else" ) );
  const wxString kw_enum( _T( "enum" ) );
  const wxString kw_elif( _T( "elif" ) );
  const wxString kw_case( _T( "case" ) );
  const wxString kw__CPP_( _T( "\"C++\"" ) );
  const wxString kw___asm( _T( "__asm" ) );
  const wxString kw_catch( _T( "catch" ) );
  const wxString kw_class( _T( "class" ) );
  const wxString kw_const( _T( "const" ) );
  const wxString kw_union( _T( "union" ) );
  const wxString kw_using( _T( "using" ) );
  const wxString kw_throw( _T( "throw" ) );
  const wxString kw_while( _T( "while" ) );
  const wxString kw_delete( _T( "delete" ) );
  const wxString kw_extern( _T( "extern" ) );
  const wxString kw_friend( _T( "friend" ) );
  const wxString kw_inline( _T( "inline" ) );
  const wxString kw_public( _T( "public" ) );
  const wxString kw_return( _T( "return" ) );
  const wxString kw_static( _T( "static" ) );
  const wxString kw_struct( _T( "struct" ) );
  const wxString kw_switch( _T( "switch" ) );
  const wxString kw_include( _T( "include" ) );
  const wxString kw_private( _T( "private" ) );
  const wxString kw_typedef( _T( "typedef" ) );
  const wxString kw_virtual( _T( "virtual" ) );
  const wxString kw_noexcept( _T( "noexcept" ) );
  const wxString kw_operator( _T( "operator" ) );
  const wxString kw_template( _T( "template" ) );
  const wxString kw_typename( _T( "typename" ) );
  const wxString kw_volatile( _T( "volatile" ) );
  const wxString kw_namespace( _T( "namespace" ) );
  const wxString kw_protected( _T( "protected" ) );
  const wxString kw_declspec( _T( "__declspec" ) );
  const wxString kw_attribute( _T( "__attribute__" ) );
}

ParserThread::ParserThread( ParserBase* parent,
                            const wxString& bufferOrFilename,
                            bool isLocal,
                            ParserThreadOptions& parserThreadOptions,
                            TokenTree* tokenTree ) :
  m_Tokenizer( tokenTree ),
  m_Parent( parent ),
  m_TokenTree( tokenTree ),
  m_LastParent( 0 ),
  m_LastScope( tsUndefined ),
  m_FileSize( 0 ),
  m_FileIdx( 0 ),
  m_IsLocal( isLocal ),
  m_Options( parserThreadOptions ),
  m_ParsingTypedef( false ),
  m_Buffer( bufferOrFilename ),
  m_StructUnionUnnamedCount( 0 ),
  m_EnumUnnamedCount( 0 ) {
  m_Tokenizer.SetTokenizerOption( parserThreadOptions.wantPreprocessor,
                                  parserThreadOptions.storeDocumentation );
  if( !m_TokenTree ) {
    m_Throw( _T( "m_TokenTree is a nullptr?!" ) );
  }
}

ParserThread::~ParserThread() {
  if( m_Options.loader ) {
    m_Options.loader->Sync();
    delete m_Options.loader;
  }
}

wxChar ParserThread::SkipToOneOfChars( const wxString& chars, bool supportNesting, bool singleCharToken ) {
  unsigned int level = m_Tokenizer.GetNestingLevel();
  while( IS_ALIVE ) {
    wxString token = m_Tokenizer.GetToken();
    if( token.IsEmpty() ) {
      return ParserConsts::null;
    }
    if( !supportNesting || m_Tokenizer.GetNestingLevel() == level ) {
      if( singleCharToken && token.length() > 1 ) {
        continue;
      }
      wxChar ch = token.GetChar( 0 );
      if( chars.Find( ch ) != wxNOT_FOUND ) {
        return ch;
      }
    }
  }
  return ParserConsts::null;
}

void ParserThread::SkipBlock() {
  TokenizerState oldState = m_Tokenizer.GetState();
  m_Tokenizer.SetState( tsNormal );
  unsigned int level = m_Tokenizer.GetNestingLevel() - 1;
  while( IS_ALIVE ) {
    wxString token = m_Tokenizer.GetToken();
    if( token.IsEmpty() ) {
      break;
    }
    if( level == m_Tokenizer.GetNestingLevel() ) {
      break;
    }
  }
  m_Tokenizer.SetState( oldState );
}

void ParserThread::SkipAngleBraces() {
  TokenizerState oldState = m_Tokenizer.GetState();
  m_Tokenizer.SetState( tsNormal );
  int nestLvl = 0;
  while( IS_ALIVE ) {
    wxString tmp = m_Tokenizer.GetToken();
    if( tmp == ParserConsts::lt ) {
      ++nestLvl;
    } else if( tmp == ParserConsts::gt ) {
      --nestLvl;
    } else if( tmp == ParserConsts::semicolon ) {
      m_Tokenizer.UngetToken();
      break;
    } else if( tmp.IsEmpty() ) {
      break;
    }
    if( nestLvl <= 0 ) {
      break;
    }
  }
  m_Tokenizer.SetState( oldState );
}

bool ParserThread::ParseBufferForNamespaces( const wxString& buffer, NameSpaceVec& result ) {
  m_Tokenizer.InitFromBuffer( buffer );
  if( !m_Tokenizer.IsOK() ) {
    return false;
  }
  result.clear();
  wxArrayString nsStack;
  m_Tokenizer.SetState( tsNormal );
  m_ParsingTypedef = false;
  while( m_Tokenizer.NotEOF() && IS_ALIVE ) {
    wxString token = m_Tokenizer.GetToken();
    if( token.IsEmpty() ) {
      continue;
    }
    if( token == ParserConsts::kw_using ) {
      SkipToOneOfChars( ParserConsts::semicolonclbrace );
    } else if( token == ParserConsts::opbrace ) {
      SkipBlock();
    } else if( token == ParserConsts::kw_namespace ) {
      wxString name = m_Tokenizer.GetToken();
      if( name == ParserConsts::opbrace ) {
        name = wxEmptyString;
      } else {
        wxString next = m_Tokenizer.PeekToken();
        if( next == ParserConsts::equals ) {
          SkipToOneOfChars( ParserConsts::semicolonclbrace );
          continue;
        } else if( next == ParserConsts::opbrace ) {
          m_Tokenizer.GetToken();
          name += ParserConsts::dcolon;
        }
      }
      nsStack.Add( name );
      NameSpace ns;
      for( size_t i = 0; i < nsStack.Count(); ++i ) {
        ns.Name << nsStack[i];
      }
      ns.StartLine = m_Tokenizer.GetLineNumber() - 1;
      ns.EndLine = -1;
      result.push_back( ns );
    } else if( token == ParserConsts::clbrace ) {
      NameSpaceVec::reverse_iterator it;
      for( it = result.rbegin(); it != result.rend(); ++it ) {
        NameSpace& ns = *it;
        if( ns.EndLine == -1 ) {
          ns.EndLine = m_Tokenizer.GetLineNumber() - 1;
          break;
        }
      }
      if( !nsStack.IsEmpty() ) {
        nsStack.RemoveAt( nsStack.GetCount() - 1 );
      }
    }
  }
  return true;
}

bool ParserThread::ParseBufferForUsingNamespace( const wxString& buffer, wxArrayString& result ) {
  m_Tokenizer.InitFromBuffer( buffer );
  if( !m_Tokenizer.IsOK() ) {
    return false;
  }
  result.Clear();
  m_Str.Clear();
  m_LastUnnamedTokenName.Clear();
  m_ParsingTypedef = false;
  while( !m_EncounteredTypeNamespaces.empty() ) {
    m_EncounteredTypeNamespaces.pop();
  }
  while( !m_EncounteredNamespaces.empty() ) {
    m_EncounteredNamespaces.pop();
  }
  while( m_Tokenizer.NotEOF() && IS_ALIVE ) {
    wxString token = m_Tokenizer.GetToken();
    if( token.IsEmpty() ) {
      continue;
    }
    if( token == ParserConsts::kw_namespace ) {
      token = m_Tokenizer.GetToken();
      SkipToOneOfChars( ParserConsts::opbrace );
      if( !token.IsEmpty() ) {
        result.Add( token );
      }
    } else if( token == ParserConsts::opbrace && m_Options.bufferSkipBlocks ) {
      SkipBlock();
    } else if( token == ParserConsts::kw_using ) {
      token = m_Tokenizer.GetToken();
      wxString peek = m_Tokenizer.PeekToken();
      if( token == ParserConsts::kw_namespace || peek == ParserConsts::dcolon ) {
        if( peek == ParserConsts::dcolon ) {
          m_Str << token;
        } else {
          while( IS_ALIVE ) {
            m_Str << m_Tokenizer.GetToken();
            if( m_Tokenizer.PeekToken() == ParserConsts::dcolon )
            { m_Str << m_Tokenizer.GetToken(); }
            else
            { break; }
          }
        }
        if( !m_Str.IsEmpty() ) {
          result.Add( m_Str );
        }
        m_Str.Clear();
      } else
      { SkipToOneOfChars( ParserConsts::semicolonclbrace ); }
    }
  }
  return true;
}

bool ParserThread::InitTokenizer() {
  if( !m_Buffer.IsEmpty() ) {
    if( !m_Options.useBuffer ) {
      if( wxFileExists( m_Buffer ) ) {
        wxFile file( m_Buffer );
        if( file.IsOpened() ) {
          m_Filename = m_Buffer;
          m_FileSize = file.Length();
          bool ret = m_Tokenizer.Init( m_Filename, m_Options.loader );
          Delete( m_Options.loader );
          return ret;
        }
      }
      return false;
    } else {
      m_Filename = m_Options.fileOfBuffer;
      m_FileIdx  = m_TokenTree->InsertFileOrGetIndex( m_Filename );
      return m_Tokenizer.InitFromBuffer( m_Buffer, m_Filename, m_Options.initLineOfBuffer );
    }
  }
  return false;
}

bool ParserThread::Parse() {
  if( !IS_ALIVE || !InitTokenizer() ) {
    return false;
  }
  bool result = false;
  m_ParsingTypedef = false;
  do {
    if( !m_TokenTree || !m_Tokenizer.IsOK() ) {
      break;
    }
    if( !m_Options.useBuffer ) {
      m_FileIdx = m_TokenTree->ReserveFileForParsing( m_Filename );
      if( !m_FileIdx ) {
        break;
      }
    }
    DoParse();
    if( !m_Options.useBuffer ) {
      m_TokenTree->FlagFileAsParsed( m_Filename );
    }
    result = true;
  } while( false );
  return result;
}

void ParserThread::DoParse() {
  TokenizerState oldState = m_Tokenizer.GetState();
  m_Tokenizer.SetState( tsNormal );
  m_Str.Clear();
  m_LastToken.Clear();
  m_LastUnnamedTokenName.Clear();
  while( !m_EncounteredTypeNamespaces.empty() ) {
    m_EncounteredTypeNamespaces.pop();
  }
  while( !m_EncounteredNamespaces.empty() ) {
    m_EncounteredNamespaces.pop();
  }
  while( m_Tokenizer.NotEOF() && IS_ALIVE ) {
    wxString token = m_Tokenizer.GetToken();
    if( token.IsEmpty() ) {
      continue;
    }
    bool switchHandled = true;
    switch( token.Length() ) {
      case 1:
        switch( static_cast<wxChar>( token[0] ) ) {
          case ParserConsts::semicolon_chr: {
            m_Str.Clear();
            m_PointerOrRef.Clear();
            while( !m_EncounteredTypeNamespaces.empty() )
            { m_EncounteredTypeNamespaces.pop(); }
            m_TemplateArgument.Clear();
          }
          break;
          case ParserConsts::dot_chr: {
            m_Str.Clear();
            SkipToOneOfChars( ParserConsts::semicolonclbrace );
          }
          break;
          case ParserConsts::gt_chr: {
            if( m_LastToken == ParserConsts::dash ) {
              m_Str.Clear();
              SkipToOneOfChars( ParserConsts::semicolonclbrace );
            } else
            { switchHandled = false; }
          }
          break;
          case ParserConsts::opbrace_chr: {
            if( !m_Options.useBuffer || m_Options.bufferSkipBlocks )
            { SkipBlock(); }
            m_Str.Clear();
          }
          break;
          case ParserConsts::clbrace_chr: {
            m_LastParent = 0L;
            m_LastScope = tsUndefined;
            m_Str.Clear();
            if( !m_Options.useBuffer || m_Options.bufferSkipBlocks ) {
              m_Tokenizer.SetState( oldState );
              return;
            }
          }
          break;
          case ParserConsts::colon_chr: {
            if( m_LastToken == ParserConsts::kw_public )
            { m_LastScope = tsPublic; }
            else if( m_LastToken == ParserConsts::kw_protected )
            { m_LastScope = tsProtected; }
            else if( m_LastToken == ParserConsts::kw_private )
            { m_LastScope = tsPrivate; }
            m_Str.Clear();
          }
          break;
          case ParserConsts::hash_chr: {
            token = m_Tokenizer.GetToken();
            if( token == ParserConsts::kw_include )
            { HandleIncludes(); }
            else
            { m_Tokenizer.SkipToEOL(); }
            m_Str.Clear();
          }
          break;
          case ParserConsts::ptr_chr:
          case ParserConsts::ref_chr: {
            m_PointerOrRef << token;
          }
          break;
          case ParserConsts::equals_chr: {
            SkipToOneOfChars( ParserConsts::commasemicolonopbrace, true );
            m_Tokenizer.UngetToken();
          }
          break;
          case ParserConsts::question_chr: {
            m_Str.Clear();
            SkipToOneOfChars( ParserConsts::semicolonopbrace, true );
          }
          break;
          case ParserConsts::plus_chr: {
            m_Str.Clear();
            SkipToOneOfChars( ParserConsts::semicolonclbrace );
          }
          break;
          case ParserConsts::dash_chr: {
            if( m_LastToken == ParserConsts::dash ) {
              m_Str.Clear();
              SkipToOneOfChars( ParserConsts::semicolonclbrace );
            }
          }
          break;
          case ParserConsts::oparray_chr: {
            SkipToOneOfChars( ParserConsts::clarray );
          }
          break;
          case ParserConsts::comma_chr:
            break;
          default:
            switchHandled = false;
            break;
        }
        break;
      case 2:
        if( token == ParserConsts::kw_if || token == ParserConsts::kw_do ) {
          if( !m_Options.useBuffer || m_Options.bufferSkipBlocks ) {
            SkipToOneOfChars( ParserConsts::semicolonclbrace, true, true );
          } else
          { HandleConditionalArguments(); }
          m_Str.Clear();
        } else
        { switchHandled = false; }
        break;
      case 3:
        if( token == ParserConsts::kw_for ) {
          if( !m_Options.useBuffer || m_Options.bufferSkipBlocks ) {
            SkipToOneOfChars( ParserConsts::semicolonclbrace, true, true );
          } else
          { HandleForLoopArguments(); }
          m_Str.Clear();
        } else
        { switchHandled = false; }
        break;
      case 4:
        if( token == ParserConsts::kw_else ) {
          if( !m_Options.useBuffer || m_Options.bufferSkipBlocks ) {
            SkipToOneOfChars( ParserConsts::semicolonclbrace, true, true );
          }
          m_Str.Clear();
        } else if( token == ParserConsts::kw_enum ) {
          m_Str.Clear();
          if( m_Options.handleEnums ) {
            HandleEnum();
          } else
          { SkipToOneOfChars( ParserConsts::semicolonclbrace, true, true ); }
        } else if( token == ParserConsts::kw_case ) {
          m_Str.Clear();
          SkipToOneOfChars( ParserConsts::colon, true, true );
        } else if( token == ParserConsts::kw___at ) {
          m_Tokenizer.GetToken();
        } else
        { switchHandled = false; }
        break;
      case 5:
        if( token == ParserConsts::kw_while || token == ParserConsts::kw_catch ) {
          if( !m_Options.useBuffer || m_Options.bufferSkipBlocks ) {
            SkipToOneOfChars( ParserConsts::semicolonclbrace, true, true );
          } else
          { HandleConditionalArguments(); }
          m_Str.Clear();
        } else if( token == ParserConsts::kw_const ) {
          m_Str << token << _T( " " );
        } else if( token == ParserConsts::kw_using ) {
          token = m_Tokenizer.GetToken();
          wxString peek = m_Tokenizer.PeekToken();
          if( peek == ParserConsts::kw_namespace ) {
            while( true ) {
              m_Str << m_Tokenizer.GetToken();
              if( m_Tokenizer.PeekToken() == ParserConsts::dcolon )
              { m_Str << m_Tokenizer.GetToken(); }
              else
              { break; }
            }
            if( !m_Str.IsEmpty()
                && m_LastParent != 0L
                && m_LastParent->m_Index != -1
                && m_LastParent->m_TokenKind == tkNamespace ) {
              if( m_LastParent->m_AncestorsString.IsEmpty() )
              { m_LastParent->m_AncestorsString << m_Str; }
              else
              { m_LastParent->m_AncestorsString << ParserConsts::comma_chr << m_Str; }
            } else if( !m_Str.IsEmpty()
                       && ( m_LastParent == 0 || m_LastParent->m_Index == -1 ) ) {
              Token* foundNsToken = nullptr;
              wxStringTokenizer tokenizer( m_Str, ParserConsts::dcolon );
              while( tokenizer.HasMoreTokens() ) {
                std::queue<wxString> nsQuqe;
                nsQuqe.push( tokenizer.GetNextToken() );
                foundNsToken = FindTokenFromQueue( nsQuqe, foundNsToken, true, foundNsToken );
                foundNsToken->m_TokenKind = tkNamespace;
              }
              m_UsedNamespacesIds.insert( foundNsToken->m_Index );
            }
          } else if( peek == ParserConsts::equals ) {
            wxString args;
            size_t lineNr = m_Tokenizer.GetLineNumber();
            Token* tdef = DoAddToken( tkTypedef, token, lineNr, 0, 0, args );
            m_Tokenizer.GetToken();
            wxString type;
            while( IS_ALIVE ) {
              type << m_Tokenizer.GetToken();
              if( m_Tokenizer.PeekToken() == ParserConsts::dcolon )
              { type << m_Tokenizer.GetToken(); }
              else
              { break; }
            }
            if( tdef ) {
              tdef->m_FullType = type;
              tdef->m_BaseType = type;
              if( tdef->IsValidAncestor( type ) )
              { tdef->m_AncestorsString = type; }
            }
          } else
          { SkipToOneOfChars( ParserConsts::semicolonclbrace ); }
          m_Str.Clear();
        } else if( token == ParserConsts::kw_class ) {
          m_Str.Clear();
          if( m_Options.handleClasses ) {
            HandleClass( ctClass );
          } else
          { SkipToOneOfChars( ParserConsts::semicolonclbrace, true, true ); }
        } else if( token == ParserConsts::kw_union ) {
          m_Str.Clear();
          if( m_Options.handleClasses ) {
            HandleClass( ctUnion );
          } else
          { SkipToOneOfChars( ParserConsts::semicolonclbrace, true, true ); }
        } else
        { switchHandled = false; }
        break;
      case 6:
        if( token == ParserConsts::kw_delete ) {
          m_Str.Clear();
          SkipToOneOfChars( ParserConsts::semicolonclbrace );
        } else if( token == ParserConsts::kw_switch ) {
          if( !m_Options.useBuffer || m_Options.bufferSkipBlocks ) {
            SkipToOneOfChars( ParserConsts::semicolonclbrace, true, true );
          } else
          { m_Tokenizer.GetToken(); }
          m_Str.Clear();
        } else if( token == ParserConsts::kw_return ) {
          SkipToOneOfChars( ParserConsts::semicolonclbrace, true, true );
          m_Str.Clear();
        } else if( token == ParserConsts::kw_extern ) {
          m_Str = m_Tokenizer.GetToken();
          if( m_Str == ParserConsts::kw__C_ || m_Str == ParserConsts::kw__CPP_ ) {
            if( m_Tokenizer.PeekToken() == ParserConsts::opbrace ) {
              m_Tokenizer.GetToken();
              DoParse();
            }
          } else
          { m_Tokenizer.UngetToken(); }
          m_Str.Clear();
        } else if( token == ParserConsts::kw_static
                   || token == ParserConsts::kw_inline ) {
        } else if( token == ParserConsts::kw_friend ) {
          SkipToOneOfChars( ParserConsts::semicolonclbrace, true, true );
          m_Str.Clear();
        } else if( token == ParserConsts::kw_struct ) {
          m_Str.Clear();
          if( m_Options.handleClasses ) {
            HandleClass( ctStructure );
          } else
          { SkipToOneOfChars( ParserConsts::semicolonclbrace, true, true ); }
        } else
        { switchHandled = false; }
        break;
      case 7:
        if( token == ParserConsts::kw_typedef ) {
          if( m_Options.handleTypedefs ) {
            HandleTypedef();
          } else
          { SkipToOneOfChars( ParserConsts::semicolonclbrace, true, true ); }
          m_Str.Clear();
        } else if( token == ParserConsts::kw_virtual ) {
        } else
        { switchHandled = false; }
        break;
      case 8:
        if( token == ParserConsts::kw_template ) {
          m_TemplateArgument = ReadAngleBrackets();
          m_Str.Clear();
          if( m_Tokenizer.PeekToken() != ParserConsts::kw_class ) {
            m_TemplateArgument.clear();
          }
        } else if( token == ParserConsts::kw_noexcept ) {
          m_Str << token << _T( " " );
        } else if( token == ParserConsts::kw_operator ) {
          wxString func = token;
          while( IS_ALIVE ) {
            token = m_Tokenizer.GetToken();
            if( !token.IsEmpty() ) {
              if( token.GetChar( 0 ) == ParserConsts::opbracket_chr ) {
                wxString peek = m_Tokenizer.PeekToken();
                if( !peek.IsEmpty()
                    && peek.GetChar( 0 ) != ParserConsts::opbracket_chr )
                { m_Tokenizer.UngetToken(); }
                else
                { func << token; }
                break;
              } else
              { func << token; }
            } else
            { break; }
          }
          HandleFunction( func, true );
          m_Str.Clear();
        } else
        { switchHandled = false; }
        break;
      case 9:
        if( token == ParserConsts::kw_namespace ) {
          m_Str.Clear();
          HandleNamespace();
        } else
        { switchHandled = false; }
        break;
      case 10:
        if( token == ParserConsts::kw_declspec ) {
          m_Tokenizer.GetToken();
        } else
        { switchHandled = false; }
        break;
      case 13:
        if( token == ParserConsts::kw_attribute ) {
          m_Tokenizer.GetToken();
        } else
        { switchHandled = false; }
        break;
      default:
        switchHandled = false;
        break;
    }
    if( token.StartsWith( ParserConsts::kw___asm ) ) {
      SkipToOneOfChars( ParserConsts::semicolon, true, true );
    } else if( !switchHandled ) {
      wxString peek = m_Tokenizer.PeekToken();
      if( !peek.IsEmpty() ) {
        if( ( peek.GetChar( 0 ) == ParserConsts::opbracket_chr )
            && m_Options.handleFunctions ) {
          if( m_Str.IsEmpty()
              && m_EncounteredNamespaces.empty()
              && m_EncounteredTypeNamespaces.empty()
              && ( !m_LastParent || m_LastParent->m_Name != token ) ) {
            wxString arg = m_Tokenizer.GetToken();
            int pos = peek.find( ParserConsts::ptr );
            if( pos != wxNOT_FOUND ) {
              peek = m_Tokenizer.PeekToken();
              if( peek.GetChar( 0 ) == ParserConsts::opbracket_chr ) {
                m_Str << token << ParserConsts::space_chr;
                HandleFunction( arg,
                                false,
                                true );
                m_Str.Clear();
              }
            } else
            { m_Str = token + arg; }
          } else if( ( m_LastToken == ParserConsts::ptr_chr )
                     && ( token.GetChar( 0 ) == ParserConsts::opbracket_chr ) ) {
            int pos = token.find( ParserConsts::ptr );
            if( pos != wxNOT_FOUND ) {
              wxString arg = token;
              HandleFunction( arg,
                              false,
                              true );
              m_Str.Clear();
            }
          } else {
            if( peek.GetChar( 1 ) == ParserConsts::ptr ) {
              wxString arg = peek;
              m_Str << token;
              token = m_Tokenizer.GetToken();
              HandleFunction( arg,
                              false,
                              true );
            } else if( !m_Options.useBuffer || m_Options.bufferSkipBlocks )
            { HandleFunction( token ); }
            else {
              if( !m_Str.IsEmpty() && m_Options.handleVars ) {
                Token* newToken = DoAddToken( tkVariable, token, m_Tokenizer.GetLineNumber() );
                if( newToken && !m_TemplateArgument.IsEmpty() )
                { ResolveTemplateArgs( newToken ); }
              }
              m_Tokenizer.GetToken();
            }
            m_Str.Clear();
          }
        } else if( ( peek  == ParserConsts::colon )
                   && ( token != ParserConsts::kw_private )
                   && ( token != ParserConsts::kw_protected )
                   && ( token != ParserConsts::kw_public ) ) {
          if( m_Options.handleVars ) {
            DoAddToken( tkVariable, token, m_Tokenizer.GetLineNumber() );
          }
          m_Tokenizer.GetToken();
          m_Tokenizer.GetToken();
        } else if( peek == ParserConsts::comma ) {
          if( !m_Str.IsEmpty() && m_Options.handleVars ) {
            Token* newToken = DoAddToken( tkVariable, token, m_Tokenizer.GetLineNumber() );
            if( newToken && !m_TemplateArgument.IsEmpty() )
            { ResolveTemplateArgs( newToken ); }
          }
          m_Tokenizer.GetToken();
        } else if( peek == ParserConsts::lt ) {
          if( m_Str.IsEmpty() || m_Str.StartsWith( ParserConsts::kw_const ) ) {
            GetTemplateArgs();
          } else
          { SkipAngleBraces(); }
          peek = m_Tokenizer.PeekToken();
          if( peek == ParserConsts::dcolon ) {
            if( m_Str.IsEmpty() )
            { m_EncounteredTypeNamespaces.push( token ); }
            else
            { m_EncounteredNamespaces.push( token ); }
            m_Tokenizer.GetToken();
          } else
          { m_Str << token << ParserConsts::space_chr; }
        } else if( peek == ParserConsts::dcolon ) {
          wxString str_stripped( m_Str );
          str_stripped.Trim( true ).Trim( false );
          if( str_stripped.IsEmpty()
              || str_stripped.IsSameAs( ParserConsts::kw_const )
              || str_stripped.IsSameAs( ParserConsts::kw_volatile ) ) {
            m_EncounteredTypeNamespaces.push( token );
          } else
          { m_EncounteredNamespaces.push( token ); }
          m_Tokenizer.GetToken();
        } else if( peek == ParserConsts::semicolon
                   || peek == ParserConsts::oparray_chr
                   || peek == ParserConsts::equals_chr ) {
          if( !m_Str.IsEmpty()
              && ( wxIsalpha( token.GetChar( 0 ) )
                   || ( token.GetChar( 0 ) == ParserConsts::underscore_chr ) ) ) {
            if( m_Options.handleVars ) {
              Token* newToken = DoAddToken( tkVariable, token, m_Tokenizer.GetLineNumber() );
              if( newToken && !m_TemplateArgument.IsEmpty() )
              { ResolveTemplateArgs( newToken ); }
            } else
            { SkipToOneOfChars( ParserConsts::semicolonclbrace, true, true ); }
          }
          if( peek == ParserConsts::oparray_chr ) {
            SkipToOneOfChars( ParserConsts::clarray );
          } else if( peek == ParserConsts::equals_chr ) {
            SkipToOneOfChars( ParserConsts::commasemicolonopbrace, true );
            m_Tokenizer.UngetToken();
          }
        } else if( !m_EncounteredNamespaces.empty() ) {
          while( !m_EncounteredNamespaces.empty() ) {
            m_EncounteredTypeNamespaces.push( m_EncounteredNamespaces.front() );
            m_EncounteredNamespaces.pop();
          }
          m_Str = token;
        } else
        { m_Str << token << ParserConsts::space_chr; }
      }
    }
    m_LastToken = token;
  }
  m_Tokenizer.SetState( oldState );
}

Token* ParserThread::TokenExists( const wxString& name, const Token* parent, short int kindMask ) {
  int foundIdx = m_TokenTree->TokenExists( name, parent ? parent->m_Index : -1, kindMask );
  if( foundIdx != wxNOT_FOUND ) {
    return m_TokenTree->at( foundIdx );
  }
  foundIdx = m_TokenTree->TokenExists( name, m_UsedNamespacesIds, kindMask );
  return m_TokenTree->at( foundIdx );
}

Token* ParserThread::TokenExists( const wxString& name, const wxString& baseArgs, const Token* parent, TokenKind kind ) {
  int foundIdx = m_TokenTree->TokenExists( name, baseArgs, parent ? parent->m_Index : -1, kind );
  if( foundIdx != wxNOT_FOUND ) {
    return m_TokenTree->at( foundIdx );
  }
  foundIdx = m_TokenTree->TokenExists( name, baseArgs, m_UsedNamespacesIds, kind );
  return m_TokenTree->at( foundIdx );
}

wxString ParserThread::GetTokenBaseType() {
  int pos = 0;
  while( pos < static_cast<int>( m_Str.Length() ) ) {
    if( wxIsspace( m_Str.GetChar( pos ) )
        && ( ( ( pos > 0 )
               && ( m_Str.GetChar( pos - 1 ) == ParserConsts::colon_chr ) )
             || ( ( pos < static_cast<int>( m_Str.Length() ) - 1 )
                  && ( m_Str.GetChar( pos + 1 ) == ParserConsts::colon_chr ) ) ) ) {
      m_Str.Remove( pos, 1 );
    } else
    { ++pos; }
  }
  pos = m_Str.Length() - 1;
  while( pos >= 0 ) {
    while( ( pos >= 0 )
           && ( wxIsspace( m_Str.GetChar( pos ) )
                || ( m_Str.GetChar( pos ) == ParserConsts::ptr_chr )
                || ( m_Str.GetChar( pos ) == ParserConsts::ref_chr ) ) ) {
      --pos;
    }
    if( pos >= 0 ) {
      int end = pos;
      while( ( pos >= 0 )
             && ( wxIsalnum( m_Str.GetChar( pos ) )
                  || ( m_Str.GetChar( pos ) == ParserConsts::underscore_chr )
                  || ( m_Str.GetChar( pos ) == ParserConsts::colon_chr ) ) ) {
        --pos;
      }
      wxString typeCandidate = m_Str.Mid( pos + 1, end - pos );
      if( typeCandidate.IsSameAs( ParserConsts::kw_const ) ) {
        continue;
      }
      return typeCandidate;
    }
  }
  return m_Str;
}

Token* ParserThread::FindTokenFromQueue( std::queue<wxString>& q, Token* parent, bool createIfNotExist,
    Token* parentIfCreated ) {
  if( q.empty() ) {
    return 0;
  }
  wxString ns = q.front();
  q.pop();
  Token* result = TokenExists( ns, parent, tkNamespace | tkClass );
  if( !result && parent == 0 ) {
    result = TokenExists( ns, parentIfCreated, tkNamespace | tkClass );
  }
  if( !result && createIfNotExist ) {
    result = new Token( ns, m_FileIdx, 0, ++m_TokenTree->m_TokenTicketCount );
    result->m_TokenKind = q.empty() ? tkClass : tkNamespace;
    result->m_IsLocal = m_IsLocal;
    result->m_ParentIndex = parentIfCreated ? parentIfCreated->m_Index : -1;
    int newidx = m_TokenTree->insert( result );
    if( parentIfCreated ) {
      parentIfCreated->AddChild( newidx );
    }
  }
  if( q.empty() ) {
    return result;
  }
  if( result ) {
    result = FindTokenFromQueue( q, result, createIfNotExist, parentIfCreated );
  }
  return result;
}

Token* ParserThread::DoAddToken( TokenKind       kind,
                                 const wxString& name,
                                 int             line,
                                 int             implLineStart,
                                 int             implLineEnd,
                                 const wxString& args,
                                 bool            isOperator,
                                 bool            isImpl ) {
  if( name.IsEmpty() ) {
    return 0;
  }
  Token* newToken = 0;
  wxString newname( name );
  m_Str.Trim( true ).Trim( false );
  if( kind == tkDestructor ) {
    newname.Prepend( ParserConsts::tilde );
    m_Str.Clear();
  }
  wxString baseArgs;
  if( kind & tkAnyFunction ) {
    if( !GetBaseArgs( args, baseArgs ) ) {
      kind = tkVariable;
    }
  }
  Token* localParent = 0;
  std::queue<wxString> q = m_EncounteredTypeNamespaces;
  if( ( kind == tkDestructor || kind == tkConstructor ) && !q.empty() ) {
    localParent = FindTokenFromQueue( q, 0, true, m_LastParent );
    if( localParent ) {
      newToken = TokenExists( newname, baseArgs, localParent, kind );
    }
  }
  if( !newToken && !m_EncounteredNamespaces.empty() ) {
    localParent = FindTokenFromQueue( m_EncounteredNamespaces, 0, true, m_LastParent );
    if( localParent ) {
      newToken = TokenExists( newname, baseArgs, localParent, kind );
    }
    if( newToken ) {
      if( isImpl && ( kind & tkAnyFunction ) ) {
        newToken->m_Args = args;
      }
    }
  }
  if( !newToken ) {
    newToken = TokenExists( newname, baseArgs, m_LastParent, kind );
    if( newToken ) {
      if( isImpl && ( kind & tkAnyFunction ) ) {
        newToken->m_Args = args;
      }
    }
  }
  if( newToken
      && ( newToken->m_TemplateArgument == m_TemplateArgument )
      && ( kind & tkAnyFunction
           || newToken->m_Args == args
           || kind & tkAnyContainer ) )
    ;
  else {
    newToken = new Token( newname, m_FileIdx, line, ++m_TokenTree->m_TokenTicketCount );
    Token* finalParent = localParent ? localParent : m_LastParent;
    if( kind == tkVariable && m_Options.parentIdxOfBuffer != -1 ) {
      finalParent = m_TokenTree->at( m_Options.parentIdxOfBuffer );
    }
    newToken->m_ParentIndex = finalParent ? finalParent->m_Index : -1;
    newToken->m_TokenKind   = kind;
    newToken->m_Scope       = m_LastScope;
    newToken->m_BaseArgs    = baseArgs;
    if( newToken->m_TokenKind == tkClass ) {
      newToken->m_BaseArgs = args;
    } else
    { newToken->m_Args = args; }
    int newidx = m_TokenTree->insert( newToken );
    if( finalParent ) {
      finalParent->AddChild( newidx );
    }
  }
  if( !( kind & ( tkConstructor | tkDestructor ) ) ) {
    wxString tokenFullType = m_Str;
    if( !m_PointerOrRef.IsEmpty() ) {
      tokenFullType << m_PointerOrRef;
      m_PointerOrRef.Clear();
    }
    wxString tokenBaseType = GetTokenBaseType();
    if( tokenBaseType.Find( ParserConsts::space_chr ) == wxNOT_FOUND ) {
      wxString prepend;
      while( !m_EncounteredTypeNamespaces.empty() ) {
        prepend << m_EncounteredTypeNamespaces.front() << ParserConsts::dcolon;
        m_EncounteredTypeNamespaces.pop();
      }
      tokenBaseType.Prepend( prepend );
    }
    newToken->m_FullType = tokenFullType;
    newToken->m_BaseType = tokenBaseType;
  }
  newToken->m_IsLocal    = m_IsLocal;
  newToken->m_IsTemp     = m_Options.isTemp;
  newToken->m_IsOperator = isOperator;
  if( !isImpl ) {
    newToken->m_FileIdx = m_FileIdx;
    newToken->m_Line    = line;
  } else {
    newToken->m_ImplFileIdx   = m_FileIdx;
    newToken->m_ImplLine      = line;
    newToken->m_ImplLineStart = implLineStart;
    newToken->m_ImplLineEnd   = implLineEnd;
    m_TokenTree->InsertTokenBelongToFile( newToken->m_ImplFileIdx, newToken->m_Index );
  }
  m_Tokenizer.SetLastTokenIdx( newToken->m_Index );
  while( !m_EncounteredTypeNamespaces.empty() ) {
    m_EncounteredTypeNamespaces.pop();
  }
  while( !m_EncounteredNamespaces.empty() ) {
    m_EncounteredNamespaces.pop();
  }
  return newToken;
}

void ParserThread::HandleIncludes() {
  wxString filename;
  bool isGlobal = !m_IsLocal;
  wxString token = m_Tokenizer.GetToken();
  if( !token.IsEmpty() ) {
    if( token.GetChar( 0 ) == '"' ) {
      size_t pos = 0;
      while( pos < token.Length() ) {
        wxChar c = token.GetChar( pos );
        if( c != _T( '"' ) ) {
          filename << c;
        }
        ++pos;
      }
    } else if( token.GetChar( 0 ) == ParserConsts::lt_chr ) {
      isGlobal = true;
      while( IS_ALIVE ) {
        token = m_Tokenizer.GetToken();
        if( token.IsEmpty() ) {
          break;
        }
        if( token.GetChar( 0 ) != ParserConsts::gt_chr ) {
          filename << token;
        } else
        { break; }
      }
    }
  }
  if( ParserCommon::FileType( filename ) == ParserCommon::ftOther ) {
    return;
  }
  if( !filename.IsEmpty() ) {
    do {
      isGlobal = true;
      if( !( isGlobal ? m_Options.followGlobalIncludes : m_Options.followLocalIncludes ) ) {
        break;
      }
      wxString real_filename = m_Parent->GetFullFileName( m_Filename, filename, isGlobal );
      if( real_filename.IsEmpty() ) {
        break;
      }
      if( m_TokenTree->IsFileParsed( real_filename ) ) {
        break;
      }
      m_Parent->ParseFile( real_filename, isGlobal, true );
    } while( false );
  }
}

void ParserThread::HandleNamespace() {
  wxString ns = m_Tokenizer.GetToken();
  int line = m_Tokenizer.GetLineNumber();
  if( ns == ParserConsts::opbrace ) {
    Token*     lastParent = m_LastParent;
    TokenScope lastScope  = m_LastScope;
    DoParse();
    m_LastParent = lastParent;
    m_LastScope   = lastScope;
  } else {
    while( true ) {
      m_Tokenizer.SetState( tsNormal );
      wxString next = m_Tokenizer.PeekToken();
      if( next == ParserConsts::opbrace ) {
        m_Tokenizer.SetState( tsNormal );
        Token* newToken = TokenExists( ns, m_LastParent, tkNamespace );
        if( !newToken ) {
          newToken = DoAddToken( tkNamespace, ns, line );
        }
        if( !newToken ) {
          return;
        }
        m_Tokenizer.GetToken();
        int lineStart = m_Tokenizer.GetLineNumber();
        Token*     lastParent = m_LastParent;
        TokenScope lastScope  = m_LastScope;
        m_LastParent = newToken;
        m_LastScope   = tsPublic;
        DoParse();
        m_LastParent = lastParent;
        m_LastScope   = lastScope;
        newToken->m_ImplFileIdx   = m_FileIdx;
        newToken->m_ImplLine      = line;
        newToken->m_ImplLineStart = lineStart;
        newToken->m_ImplLineEnd   = m_Tokenizer.GetLineNumber();
        break;
      } else if( next == ParserConsts::equals ) {
        m_Tokenizer.GetToken();
        m_Tokenizer.SetState( tsNormal );
        Token* lastParent = m_LastParent;
        Token* aliasToken = NULL;
        while( IS_ALIVE ) {
          wxString aliasStr = m_Tokenizer.GetToken();
          aliasToken = TokenExists( aliasStr, m_LastParent, tkNamespace );
          if( !aliasToken ) {
            aliasToken = DoAddToken( tkNamespace, aliasStr, line );
          }
          if( !aliasToken ) {
            return;
          }
          if( m_Tokenizer.PeekToken() == ParserConsts::dcolon ) {
            m_Tokenizer.GetToken();
            m_LastParent = aliasToken;
          } else {
            break;
          }
        }
        aliasToken->m_Aliases.Add( ns );
        m_LastParent = lastParent;
        break;
      } else {
        m_Tokenizer.SetState( tsNormal );
        SkipToOneOfChars( ParserConsts::semicolonopbrace );
        m_Tokenizer.UngetToken();
        wxString peek = m_Tokenizer.PeekToken();
        if( peek == ParserConsts::opbrace ) {
          continue;
        } else {
          break;
        }
      }
    }
  }
}

void ParserThread::HandleClass( EClassType ct ) {
  TokenizerState oldState = m_Tokenizer.GetState();
  m_Tokenizer.SetState( tsNormal );
  int lineNr = m_Tokenizer.GetLineNumber();
  wxString ancestors;
  wxString lastCurrent;
  while( IS_ALIVE ) {
    wxString current = m_Tokenizer.GetToken();
    wxString next    = m_Tokenizer.PeekToken();
    if( current == ParserConsts::kw_attribute || current == ParserConsts::kw_declspec ) {
      m_Tokenizer.GetToken();
      current = m_Tokenizer.GetToken();
      next    = m_Tokenizer.PeekToken();
    }
    if( current.IsEmpty() || next.IsEmpty() ) {
      break;
    }
    if( next == ParserConsts::lt ) {
      GetTemplateArgs();
      next = m_Tokenizer.PeekToken();
    }
    if( next == ParserConsts::colon ) {
      m_Tokenizer.GetToken();
      while( IS_ALIVE ) {
        wxString tmp = m_Tokenizer.GetToken();
        next = m_Tokenizer.PeekToken();
        if( tmp == ParserConsts::kw_public
            || tmp == ParserConsts::kw_protected
            || tmp == ParserConsts::kw_private ) {
          continue;
        }
        if( !( tmp == ParserConsts::comma || tmp == ParserConsts::gt ) ) {
          if( tmp == ParserConsts::dcolon || next == ParserConsts::dcolon ) {
            ancestors << tmp;
          } else {
            ancestors << tmp << ParserConsts::comma_chr;
          }
        }
        if( next.IsEmpty()
            || next == ParserConsts::opbrace
            || next == ParserConsts::semicolon ) {
          break;
        } else if( next == ParserConsts::lt ) {
          SkipAngleBraces();
          m_Tokenizer.UngetToken();
        }
      }
    }
    if( current == ParserConsts::opbrace ) {
      wxString unnamedTmp;
      unnamedTmp.Printf( _T( "%s%s%u_%lu" ),
                         g_UnnamedSymbol.wx_str(),
                         ( ct == ctClass ? _T( "Class" ) : ( ct == ctUnion ? _T( "Union" ) : _T( "Struct" ) ) ),
                         m_FileIdx, static_cast<unsigned long>( m_StructUnionUnnamedCount++ ) );
      Token* newToken = DoAddToken( tkClass, unnamedTmp, lineNr );
      if( !newToken ) {
        m_Tokenizer.SetState( oldState );
        return;
      }
      newToken->m_TemplateArgument = m_TemplateArgument;
      wxArrayString formals;
      SplitTemplateFormalParameters( m_TemplateArgument, formals );
      newToken->m_TemplateType = formals;
      m_TemplateArgument.Clear();
      Token*     lastParent     = m_LastParent;
      TokenScope lastScope      = m_LastScope;
      bool       parsingTypedef = m_ParsingTypedef;
      m_LastParent     = newToken;
      m_LastScope      = ct == ctClass ? tsPrivate : tsPublic;
      m_ParsingTypedef = false;
      newToken->m_ImplLine = lineNr;
      newToken->m_ImplLineStart = m_Tokenizer.GetLineNumber();
      newToken->m_IsAnonymous = true;
      DoParse();
      m_LastParent     = lastParent;
      m_LastScope      = lastScope;
      m_ParsingTypedef = parsingTypedef;
      m_LastUnnamedTokenName = unnamedTmp;
      if( m_ParsingTypedef ) {
        m_Str.Clear();
        break;
      } else {
        m_Str = newToken->m_Name;
        m_Str.Clear();
        break;
      }
    } else if( next == ParserConsts::opbrace ) {
      wxStringTokenizer tkz( ancestors, ParserConsts::comma );
      wxString args;
      while( tkz.HasMoreTokens() ) {
        const wxString& ancestor = tkz.GetNextToken();
        if( ancestor.IsEmpty() ) {
          continue;
        }
        if( args.IsEmpty() ) {
          args += ParserConsts::space + ParserConsts::colon;
        } else
        { args += ParserConsts::comma; }
        args += ParserConsts::space + ancestor;
      }
      wxArrayString formals;
      SplitTemplateFormalParameters( m_TemplateArgument, formals );
      if( !formals.IsEmpty() ) {
        args.Prepend( ParserConsts::lt + GetStringFromArray( formals, ParserConsts::comma, false ) + ParserConsts::gt );
      }
      Token* newToken = DoAddToken( tkClass, current, lineNr, 0, 0, args );
      if( !newToken ) {
        m_Tokenizer.SetState( oldState );
        return;
      }
      newToken->m_AncestorsString = ancestors;
      m_Tokenizer.GetToken();
      Token* lastParent = m_LastParent;
      TokenScope lastScope = m_LastScope;
      bool parsingTypedef = m_ParsingTypedef;
      m_LastParent = newToken;
      m_LastScope = ct == ctClass ? tsPrivate : tsPublic;
      m_ParsingTypedef = false;
      newToken->m_ImplLine = lineNr;
      newToken->m_ImplLineStart = m_Tokenizer.GetLineNumber();
      newToken->m_TemplateArgument = m_TemplateArgument;
      newToken->m_TemplateType = formals;
      m_TemplateArgument.Clear();
      DoParse();
      newToken->m_ImplLineEnd = m_Tokenizer.GetLineNumber();
      m_ParsingTypedef = parsingTypedef;
      m_LastParent = lastParent;
      m_LastScope = lastScope;
      if( m_ParsingTypedef ) {
        m_Str.Clear();
        break;
      } else {
        m_Str = newToken->m_Name;
        m_Str.Clear();
        break;
      }
    } else if( next == ParserConsts::semicolon ) {
      if( m_LastParent && m_LastParent->m_TokenKind == tkClass && !lastCurrent.IsEmpty() ) {
        m_Str << lastCurrent << ParserConsts::space_chr;
        DoAddToken( tkVariable, current, m_Tokenizer.GetLineNumber() );
        break;
      } else
      { break; }
    } else if( next.GetChar( 0 ) == ParserConsts::opbracket_chr ) {
      HandleFunction( current );
      break;
    } else if( ( next.GetChar( 0 ) == ParserConsts::ptr_chr ) || ( next.GetChar( 0 ) == ParserConsts::ref_chr ) ) {
      if( next.GetChar( 0 ) == ParserConsts::ptr_chr && m_ParsingTypedef ) {
        wxString args;
        Token* newToken = DoAddToken( tkClass, current, lineNr, 0, 0, args );
        if( !newToken ) {
          m_Tokenizer.SetState( oldState );
          return;
        }
        newToken->m_AncestorsString = ancestors;
        m_PointerOrRef << m_Tokenizer.GetToken();
        m_Str.Clear();
        break;
      } else {
        m_Str << current;
      }
      break;
    } else if( next == ParserConsts::equals ) {
      if( !lastCurrent.IsEmpty() ) {
        m_Str << lastCurrent << ParserConsts::space_chr;
        DoAddToken( tkVariable, current, m_Tokenizer.GetLineNumber() );
      }
      SkipToOneOfChars( ParserConsts::semicolon, true, true );
      break;
    } else {
      if( m_ParsingTypedef ) {
        m_Tokenizer.UngetToken();
        break;
      }
      if( TokenExists( current, m_LastParent, tkClass ) ) {
        if( !TokenExists( next, m_LastParent, tkVariable ) ) {
          wxString farnext;
          m_Tokenizer.GetToken();
          farnext = m_Tokenizer.PeekToken();
          if( farnext == ParserConsts::semicolon ) {
            if( m_Options.handleVars ) {
              m_Str = current;
              DoAddToken( tkVariable, next, m_Tokenizer.GetLineNumber() );
              m_Str.Clear();
            }
            m_Tokenizer.GetToken();
            break;
          } else {
            m_Tokenizer.UngetToken();
          }
        }
      }
    }
    lastCurrent = current;
  }
  m_Tokenizer.SetState( oldState );
}

void ParserThread::HandleFunction( wxString& name, bool isOperator, bool isPointer ) {
  int lineNr = m_Tokenizer.GetLineNumber();
  wxString args = m_Tokenizer.GetToken();
  wxString peek = m_Tokenizer.PeekToken();
  if( isPointer ) {
    int pos = name.find( ParserConsts::ptr );
    if( pos != wxNOT_FOUND && ( peek == ParserConsts::semicolon || peek == ParserConsts::equals ) ) {
      name.RemoveLast();
      name.Remove( 0, pos + 1 ).Trim( false );
      pos = name.find( ParserConsts::oparray_chr );
      if( pos != wxNOT_FOUND ) {
        name.Remove( pos ).Trim( true );
      }
      Token* newToken =  DoAddToken( tkFunction, name, lineNr, 0, 0, args );
      if( newToken ) {
        newToken->m_IsConst = false;
        newToken->m_TemplateArgument = m_TemplateArgument;
        if( !m_TemplateArgument.IsEmpty() && newToken->m_TemplateMap.empty() ) {
          ResolveTemplateArgs( newToken );
        }
      }
      m_TemplateArgument.Clear();
    }
    return;
  }
  if( !m_Str.StartsWith( ParserConsts::kw_friend ) ) {
    int lineStart = 0;
    int lineEnd = 0;
    bool isCtor = m_Str.IsEmpty();
    bool isDtor = m_Str.StartsWith( ParserConsts::tilde );
    Token* localParent = 0;
    if( ( isCtor || isDtor ) && !m_EncounteredTypeNamespaces.empty() ) {
      std::queue<wxString> q = m_EncounteredTypeNamespaces;
      localParent = FindTokenFromQueue( q, m_LastParent );
    } else {
      std::queue<wxString> q = m_EncounteredNamespaces;
      localParent = FindTokenFromQueue( q, m_LastParent );
    }
    bool isCtorOrDtor = m_LastParent && name == m_LastParent->m_Name;
    if( !isCtorOrDtor ) {
      isCtorOrDtor = localParent && name == localParent->m_Name;
    }
    if( !isCtorOrDtor && m_Options.useBuffer ) {
      isCtorOrDtor = isCtor || isDtor;
    }
    bool isImpl = false;
    bool isConst = false;
    bool isNoExcept = false;
    while( !peek.IsEmpty() ) {
      if( peek == ParserConsts::colon ) {
        SkipToOneOfChars( ParserConsts::opbrace );
        m_Tokenizer.UngetToken();
        peek = m_Tokenizer.PeekToken();
        continue;
      } else if( peek == ParserConsts::opbrace ) {
        isImpl = true;
        m_Tokenizer.GetToken();
        lineStart = m_Tokenizer.GetLineNumber();
        SkipBlock();
        lineEnd = m_Tokenizer.GetLineNumber();
        break;
      } else if( peek == ParserConsts::clbrace || peek == ParserConsts::semicolon ) {
        break;
      } else if( peek == ParserConsts::kw_const ) {
        isConst = true;
      } else if( peek == ParserConsts::kw_noexcept ) {
        isNoExcept = true;
      } else if( peek == ParserConsts::kw_throw ) {
        wxString arg = m_Tokenizer.GetToken();
      } else if( peek == ParserConsts::kw_try ) {
        m_Tokenizer.GetToken();
        if( m_Tokenizer.PeekToken() == ParserConsts::colon ) {
          SkipToOneOfChars( ParserConsts::opbrace );
          m_Tokenizer.UngetToken();
        }
        if( m_Tokenizer.PeekToken() == ParserConsts::opbrace ) {
          isImpl = true;
          m_Tokenizer.GetToken();
          lineStart = m_Tokenizer.GetLineNumber();
          SkipBlock();
          while( m_Tokenizer.PeekToken() == ParserConsts::kw_catch ) {
            m_Tokenizer.GetToken();
            m_Tokenizer.GetToken();
            if( m_Tokenizer.PeekToken() == ParserConsts::opbrace ) {
              m_Tokenizer.GetToken();
              SkipBlock();
            }
          }
          lineEnd = m_Tokenizer.GetLineNumber();
          break;
        }
      } else {
        break;
      }
      m_Tokenizer.GetToken();
      peek = m_Tokenizer.PeekToken();
    }
    TokenKind tokenKind = !isCtorOrDtor ? tkFunction : ( isDtor ? tkDestructor : tkConstructor );
    Token* newToken =  DoAddToken( tokenKind, name, lineNr, lineStart, lineEnd, args, isOperator, isImpl );
    if( newToken ) {
      newToken->m_IsConst = isConst;
      newToken->m_IsNoExcept = isNoExcept;
      newToken->m_TemplateArgument = m_TemplateArgument;
      if( !m_TemplateArgument.IsEmpty() && newToken->m_TemplateMap.empty() ) {
        ResolveTemplateArgs( newToken );
      }
    }
    m_TemplateArgument.Clear();
  }
}

void ParserThread::HandleConditionalArguments() {
  if( !m_Str.empty() ) {
    return;
  }
  if( !m_PointerOrRef.empty() ) {
    return;
  }
  if( !m_TemplateArgument.empty() ) {
    return;
  }
  wxString args = m_Tokenizer.GetToken();
  if( args.StartsWith( _T( "(" ) ) ) {
    args = args.Mid( 1, args.length() - 1 );
  }
  if( args.EndsWith( _T( ")" ) ) ) {
    args = args.Mid( 0, args.length() - 1 );
  }
  TokenTree tree;
  wxString fileName = m_Tokenizer.GetFilename();
  Tokenizer smallTokenizer( &tree );
  smallTokenizer.InitFromBuffer( args, fileName, m_Tokenizer.GetLineNumber() );
  while( IS_ALIVE ) {
    wxString token = smallTokenizer.GetToken();
    if( token.empty() ) {
      break;
    }
    wxString peek = smallTokenizer.PeekToken();
    if( peek.empty() ) {
      if( !m_Str.empty() ) {
        wxString varType, templateArgs;
        RemoveTemplateArgs( m_Str, varType, templateArgs );
        m_Str = varType;
        m_TemplateArgument = templateArgs;
        Token *newToken = DoAddToken( tkVariable, token, smallTokenizer.GetLineNumber() );
        if( newToken && !m_TemplateArgument.IsEmpty() ) {
          ResolveTemplateArgs( newToken );
        }
      }
      break;
    } else {
      if( token == ParserConsts::ref_chr || token == ParserConsts::ptr_chr ) {
        m_PointerOrRef << token;
      } else {
        if( !m_Str.empty() ) {
          m_Str << _T( " " );
        }
        m_Str << token;
      }
    }
  }
  m_Str.clear();
  m_PointerOrRef.clear();
  m_TemplateArgument.clear();
}

void ParserThread::HandleForLoopArguments() {
  if( !m_Str.empty() ) {
    return;
  }
  if( !m_PointerOrRef.empty() ) {
    return;
  }
  if( !m_TemplateArgument.empty() ) {
    return;
  }
  wxString args = m_Tokenizer.GetToken();
  if( args.StartsWith( _T( "(" ) ) ) {
    args = args.Mid( 1, args.length() - 1 );
  }
  if( args.EndsWith( _T( ")" ) ) ) {
    args = args.Mid( 0, args.length() - 1 );
  }
  TokenTree tree;
  wxString fileName = m_Tokenizer.GetFilename();
  Tokenizer smallTokenizer( &tree );
  smallTokenizer.InitFromBuffer( args, fileName, m_Tokenizer.GetLineNumber() );
  while( IS_ALIVE ) {
    wxString token = smallTokenizer.GetToken();
    if( token.empty() ) {
      break;
    }
    wxString peek = smallTokenizer.PeekToken();
    bool createNewToken = false;
    bool finished = false;
    if( peek == ParserConsts::equals ) {
      while( IS_ALIVE ) {
        smallTokenizer.GetToken();
        peek = smallTokenizer.PeekToken();
        if( peek == ParserConsts::comma
            || peek == ParserConsts::semicolon
            || peek.empty() ) {
          break;
        }
      }
    }
    if( peek == ParserConsts::comma ) {
      smallTokenizer.GetToken();
      createNewToken = true;
    } else if( peek == ParserConsts::colon
               || peek == ParserConsts::semicolon
               || peek.empty() ) {
      createNewToken = true;
      finished = true;
    } else {
      if( token == ParserConsts::ref_chr || token == ParserConsts::ptr_chr ) {
        m_PointerOrRef << token;
      } else {
        if( !m_Str.empty() ) {
          m_Str << _T( " " );
        }
        m_Str << token;
      }
    }
    if( createNewToken && !m_Str.empty() ) {
      wxString name, templateArgs;
      RemoveTemplateArgs( m_Str, name, templateArgs );
      m_Str = name;
      m_TemplateArgument = templateArgs;
      Token *newToken = DoAddToken( tkVariable, token, smallTokenizer.GetLineNumber() );
      if( newToken && !m_TemplateArgument.IsEmpty() ) {
        ResolveTemplateArgs( newToken );
      }
    }
    if( finished ) {
      break;
    }
  }
  m_Str.clear();
  m_PointerOrRef.clear();
  m_TemplateArgument.clear();
}

void ParserThread::HandleEnum() {
  bool isUnnamed = false;
  bool isEnumClass = false;
  int lineNr = m_Tokenizer.GetLineNumber();
  wxString token = m_Tokenizer.GetToken();
  if( token == ParserConsts::kw_class ) {
    token = m_Tokenizer.GetToken();
    isEnumClass = true;
  }
  if( token.IsEmpty() ) {
    return;
  } else if( token == ParserConsts::opbrace ) {
    if( m_ParsingTypedef ) {
      token.Printf( _T( "%sEnum%u_%lu" ), g_UnnamedSymbol.wx_str(), m_FileIdx, static_cast<unsigned long>( m_EnumUnnamedCount++ ) );
      m_LastUnnamedTokenName = token;
    } else
    { token = g_UnnamedSymbol; }
    m_Tokenizer.UngetToken();
    isUnnamed = true;
  }
  Token* newEnum = 0L;
  unsigned int level = 0;
  if( wxIsalpha( token.GetChar( 0 ) )
      || ( token.GetChar( 0 ) == ParserConsts::underscore_chr ) ) {
    if( m_Tokenizer.PeekToken().GetChar( 0 ) != ParserConsts::opbrace_chr ) {
      if( TokenExists( token, m_LastParent, tkEnum ) ) {
        if( !TokenExists( m_Tokenizer.PeekToken(), m_LastParent, tkVariable ) ) {
          wxString ident = m_Tokenizer.GetToken();
          if( m_Tokenizer.PeekToken() == ParserConsts::semicolon ) {
            if( m_Options.handleEnums ) {
              m_Str = token;
              DoAddToken( tkVariable, ident, m_Tokenizer.GetLineNumber() );
              m_Str.Clear();
            }
            m_Tokenizer.GetToken();
          } else
          { m_Tokenizer.UngetToken(); }
        }
      }
      return;
    }
    if( isUnnamed && !m_ParsingTypedef ) {
      newEnum = TokenExists( token, m_LastParent, tkEnum );
    }
    if( !newEnum ) {
      newEnum = DoAddToken( tkEnum, token, lineNr );
      newEnum->m_IsAnonymous = true;
    }
    level = m_Tokenizer.GetNestingLevel();
    m_Tokenizer.GetToken();
  } else {
    if( token.GetChar( 0 ) != ParserConsts::opbrace_chr ) {
      return;
    }
    level = m_Tokenizer.GetNestingLevel() - 1;
  }
  int lineStart = m_Tokenizer.GetLineNumber();
  int enumValue = 0;
  bool updateValue = true;
  const TokenizerState oldState = m_Tokenizer.GetState();
  m_Tokenizer.SetState( tsNormal );
  while( IS_ALIVE ) {
    token = m_Tokenizer.GetToken();
    wxString peek = m_Tokenizer.PeekToken();
    if( token.IsEmpty() || peek.IsEmpty() ) {
      return;
    }
    if( token == ParserConsts::clbrace && level == m_Tokenizer.GetNestingLevel() ) {
      break;
    }
    if( peek == ParserConsts::colon ) {
      peek = SkipToOneOfChars( ParserConsts::equals + ParserConsts::commaclbrace );
    }
    if( peek == ParserConsts::equals ) {
      m_Tokenizer.GetToken();
      long result;
      updateValue = false;
      if( CalcEnumExpression( newEnum, result, peek ) ) {
        enumValue = result;
        updateValue = true;
      }
    }
    if( peek == ParserConsts::comma || peek == ParserConsts::clbrace ) {
      if( wxIsalpha( token.GetChar( 0 ) )
          || ( token.GetChar( 0 ) == ParserConsts::underscore_chr ) ) {
        wxString args;
        if( updateValue ) {
          args << enumValue++;
        }
        Token* lastParent = m_LastParent;
        m_LastParent = newEnum;
        Token* enumerator = DoAddToken( tkEnumerator, token, m_Tokenizer.GetLineNumber(), 0, 0, args );
        enumerator->m_Scope = isEnumClass ? tsPrivate : tsPublic;
        m_LastParent = lastParent;
      }
    }
  }
  m_Tokenizer.SetState( oldState );
  newEnum->m_ImplLine      = lineNr;
  newEnum->m_ImplLineStart = lineStart;
  newEnum->m_ImplLineEnd   = m_Tokenizer.GetLineNumber();
}

bool ParserThread::CalcEnumExpression( Token* tokenParent, long& result, wxString& peek ) {
  const TokenizerState oldState = m_Tokenizer.GetState();
  m_Tokenizer.SetState( tsRawExpression );
  Expression exp;
  wxString token, next;
  while( IS_ALIVE ) {
    token = m_Tokenizer.GetToken();
    if( token.IsEmpty() ) {
      return false;
    }
    if( token == _T( "\\" ) ) {
      continue;
    }
    if( token == ParserConsts::comma || token == ParserConsts::clbrace ) {
      m_Tokenizer.UngetToken();
      peek = token;
      break;
    }
    if( token == ParserConsts::dcolon ) {
      peek = SkipToOneOfChars( ParserConsts::commaclbrace );
      m_Tokenizer.UngetToken();
      exp.Clear();
      break;
    }
    if( wxIsalpha( token[0] ) || token[0] == ParserConsts::underscore_chr ) {
      const Token* tk = m_TokenTree->at( m_TokenTree->TokenExists( token, tokenParent->m_Index, tkEnumerator ) );
      if( tk ) {
        if( !tk->m_Args.IsEmpty() && wxIsdigit( tk->m_Args[0] ) ) {
          token = tk->m_Args;
        }
      } else {
        peek = SkipToOneOfChars( ParserConsts::commaclbrace );
        m_Tokenizer.UngetToken();
        exp.Clear();
        break;
      }
    }
    if( !token.StartsWith( _T( "0x" ) ) ) {
      exp.AddToInfixExpression( token );
    } else {
      long value;
      if( token.ToLong( &value, 16 ) ) {
        exp.AddToInfixExpression( F( _T( "%ld" ), value ) );
      } else {
        peek = SkipToOneOfChars( ParserConsts::commaclbrace );
        exp.Clear();
        break;
      }
    }
  }
  m_Tokenizer.SetState( oldState );
  exp.ConvertInfixToPostfix();
  if( exp.CalcPostfix() && exp.GetStatus() ) {
    result = exp.GetResult();
    return true;
  }
  return false;
}

void ParserThread::HandleTypedef() {
  size_t lineNr = m_Tokenizer.GetLineNumber();
  bool is_function_pointer = false;
  wxString typ;
  std::queue<wxString> components;
  wxString args;
  wxString token;
  wxString peek;
  m_ParsingTypedef = true;
  while( IS_ALIVE ) {
    token = m_Tokenizer.GetToken();
    peek  = m_Tokenizer.PeekToken();
    if( token.IsEmpty() || token == ParserConsts::semicolon ) {
      m_Tokenizer.UngetToken();
      break;
    }
    if( token == ParserConsts::kw_const ) {
      continue;
    }
    if( token == ParserConsts::kw_class
        || token == ParserConsts::kw_struct
        || token == ParserConsts::kw_union ) {
      HandleClass( token == ParserConsts::kw_class ? ctClass :
                   token == ParserConsts::kw_union ? ctUnion :
                   ctStructure );
      token = m_LastUnnamedTokenName;
    } else if( token == ParserConsts::ptr || token == ParserConsts::ref ) {
      m_PointerOrRef << token;
      continue;
    } else if( peek == ParserConsts::comma ) {
      m_Tokenizer.UngetToken();
      if( components.size() != 0 ) {
        wxString ancestor;
        while( components.size() > 0 ) {
          wxString tempToken = components.front();
          components.pop();
          if( !ancestor.IsEmpty() ) {
            ancestor << ParserConsts::space_chr;
          }
          ancestor << tempToken;
        }
        if( !ReadClsNames( ancestor ) ) {
          m_Tokenizer.GetToken();
        }
      }
    } else if( token == ParserConsts::kw_enum ) {
      HandleEnum();
      token = m_LastUnnamedTokenName;
    }
    while( peek == ParserConsts::dcolon ) {
      token << peek;
      m_Tokenizer.GetToken();
      token << m_Tokenizer.GetToken();
      peek = m_Tokenizer.PeekToken();
    }
    if( token.GetChar( 0 ) == ParserConsts::opbracket_chr ) {
      is_function_pointer = true;
      if( peek.GetChar( 0 ) == ParserConsts::opbracket_chr ) {
        token.RemoveLast();
        int pos = token.Find( ParserConsts::ptr_chr, true );
        if( pos != wxNOT_FOUND ) {
          typ << ParserConsts::opbracket_chr
              << token.Mid( 1, pos )
              << ParserConsts::clbracket_chr;
          token.Remove( 0, pos + 1 );
        } else {
          typ = _T( "(*)" );
          token.Remove( 0, 1 );
        }
        args = peek;
        m_Tokenizer.GetToken();
        components.push( token.Trim( true ).Trim( false ) );
      } else {
        args = token;
      }
      break;
    }
    components.push( token.Trim( true ).Trim( false ) );
    if( peek == ParserConsts::lt ) {
      GetTemplateArgs();
      continue;
    }
  }
  m_ParsingTypedef = false;
  if( components.empty() ) {
    return;
  }
  if( !is_function_pointer && components.size() <= 1 ) {
    return;
  }
  wxString ancestor;
  wxString alias;
  if( ( components.size() == 2 )
      && m_LastParent
      && m_LastParent->m_TokenKind == tkClass
      && ( !m_LastParent->m_TemplateType.IsEmpty() )
      && m_LastParent->m_TemplateType.Index( components.front() ) != wxNOT_FOUND ) {
    wxArrayString templateType = m_LastParent->m_TemplateType;
    alias = components.front();
    components.pop();
    ancestor = components.front();
  } else {
    while( components.size() > 1 ) {
      token = components.front();
      components.pop();
      if( !ancestor.IsEmpty() ) {
        ancestor << ParserConsts::space_chr;
      }
      ancestor << token;
    }
  }
  m_Str.Clear();
  Token* tdef = DoAddToken( tkTypedef, components.front(), lineNr, 0, 0, args );
  if( tdef ) {
    wxString actualAncestor = ancestor.BeforeFirst( ParserConsts::lt_chr ).Trim();
    if( is_function_pointer ) {
      tdef->m_FullType        = ancestor + typ;
      tdef->m_BaseType        = actualAncestor;
      if( tdef->IsValidAncestor( ancestor ) ) {
        tdef->m_AncestorsString = ancestor;
      }
    } else {
      tdef->m_FullType        = ancestor;
      tdef->m_BaseType        = actualAncestor;
      tdef->m_TemplateAlias   = alias;
      if( tdef->IsValidAncestor( ancestor ) ) {
        tdef->m_AncestorsString = ancestor;
      }
      if( !m_TemplateArgument.IsEmpty() ) {
        ResolveTemplateArgs( tdef );
      }
    }
  }
}

bool ParserThread::ReadVarNames() {
  bool success = true;
  while( IS_ALIVE ) {
    wxString token = m_Tokenizer.GetToken();
    if( token.IsEmpty() ) {
      break;
    }
    if( token == ParserConsts::comma ) {
      continue;
    } else if( token == ParserConsts::semicolon ) {
      m_PointerOrRef.Clear();
      break;
    } else if( token == ParserConsts::oparray ) {
      SkipToOneOfChars( ParserConsts::clarray );
    } else if( token == ParserConsts::ptr ) {
      m_PointerOrRef << token;
    } else if( wxIsalpha( token.GetChar( 0 ) ) || ( token.GetChar( 0 ) == ParserConsts::underscore_chr ) ) {
      if( m_Str.StartsWith( g_UnnamedSymbol ) ) {
        RefineAnonymousTypeToken( tkUndefined, token );
      }
      Token* newToken = DoAddToken( tkVariable, token, m_Tokenizer.GetLineNumber() );
      if( !newToken ) {
        break;
      }
    } else {
      success = false;
      break;
    }
  }
  return success;
}

bool ParserThread::ReadClsNames( wxString& ancestor ) {
  bool success = true;
  while( IS_ALIVE ) {
    wxString token = m_Tokenizer.GetToken();
    if( token.IsEmpty() ) {
      break;
    }
    if( token == ParserConsts::comma ) {
      continue;
    } else if( token == ParserConsts::semicolon ) {
      m_Tokenizer.UngetToken();
      m_PointerOrRef.Clear();
      break;
    } else if( token == ParserConsts::ptr ) {
      m_PointerOrRef << token;
    } else if( wxIsalpha( token.GetChar( 0 ) ) || ( token.GetChar( 0 ) == ParserConsts::underscore_chr ) ) {
      m_Str.clear();
      m_Str = ancestor;
      if( m_Str.StartsWith( g_UnnamedSymbol ) ) {
        RefineAnonymousTypeToken( tkTypedef | tkClass, token );
        ancestor = m_Str;
      }
      Token* newToken = DoAddToken( tkTypedef, token, m_Tokenizer.GetLineNumber() );
      if( !newToken ) {
        break;
      } else {
        newToken->m_AncestorsString = ancestor;
      }
    } else {
      m_Tokenizer.UngetToken();
      success = false;
      break;
    }
  }
  return success;
}

bool ParserThread::GetBaseArgs( const wxString& args, wxString& baseArgs ) {
  const wxChar* ptr = args;
  wxString word;
  bool skip = false;
  bool sym  = false;
  bool one  = true;
  baseArgs.Alloc( args.Len() + 1 );
  while( *ptr != ParserConsts::null ) {
    switch( *ptr ) {
      case ParserConsts::eol_chr:
        while( *ptr != ParserConsts::null && *ptr <= ParserConsts::space_chr ) {
          ++ptr;
        }
        break;
      case ParserConsts::space_chr:
        if( ( word == ParserConsts::kw_enum )
            || ( word == ParserConsts::kw_const )
            || ( word == ParserConsts::kw_volatile ) ) {
          skip = false;
        } else
        { skip = true; }
        word = _T( "" );
        sym  = false;
        break;
      case ParserConsts::ptr_chr:
        while( *( ptr + 1 ) != ParserConsts::null && *( ptr + 1 ) == ParserConsts::ptr_chr ) {
          baseArgs << *ptr;
          ptr++;
        }
      case ParserConsts::ref_chr:
        word = _T( "" );
        skip = true;
        sym  = true;
        {
          wxString lastStrippedArg;
          int lastArgComma = baseArgs.Find( ParserConsts::comma_chr, true );
          if( lastArgComma ) {
            lastStrippedArg = baseArgs.Mid( 1 );
          } else
          { lastStrippedArg = baseArgs.Mid( lastArgComma ); }
          if( lastStrippedArg.Find( ParserConsts::opbracket_chr ) == wxNOT_FOUND ) {
            baseArgs << *ptr;
            int brackets = 0;
            ptr++;
            while( *ptr != ParserConsts::null ) {
              if( *ptr == ParserConsts::opbracket_chr )
              { brackets++; }
              else if( *ptr == ParserConsts::clbracket_chr ) {
                if( brackets == 0 )
                { break; }
                brackets--;
              } else if( *ptr == ParserConsts::comma_chr && brackets == 0 ) {
                skip = false;
                break;
              }
              ptr++;
            }
          }
        }
        break;
      case ParserConsts::colon_chr:
        skip = false;
        sym  = true;
        break;
      case ParserConsts::oparray_chr:
        while( *ptr != ParserConsts::null
               && *ptr != ParserConsts::clarray_chr ) {
          if( *ptr != ParserConsts::space_chr ) {
            baseArgs << *ptr;
          }
          ptr++;
        }
        skip = true;
        sym  = true;
        break;
      case ParserConsts::lt_chr:
        while( *ptr != ParserConsts::null && *ptr != ParserConsts::gt_chr ) {
          if( *ptr != ParserConsts::space_chr ) {
            baseArgs << *ptr;
          }
          ptr++;
        }
        skip = true;
        sym  = true;
        break;
      case ParserConsts::comma_chr:
      case ParserConsts::clbracket_chr:
      case ParserConsts::opbracket_chr:
        if( skip && *ptr == ParserConsts::comma_chr ) {
          one = false;
        }
        if( *ptr == ParserConsts::opbracket_chr && word == ParserConsts::kw_attribute ) {
          baseArgs = baseArgs.Mid( 0, baseArgs.Len() - 13 );
          int brackets = 1;
          ptr++;
          while( *ptr != ParserConsts::null ) {
            if( *ptr == ParserConsts::opbracket_chr )
            { brackets++; }
            else if( *ptr == ParserConsts::clbracket_chr ) {
              brackets--;
              if( brackets == 0 ) {
                ptr++;
                break;
              }
            }
            ptr++;
          }
          while( *ptr != ParserConsts::null && *( ptr ) == ParserConsts::space_chr ) {
            ++ptr;
          }
          word = _T( "" );
          sym  = false;
          skip = false;
          break;
        }
        word = _T( "" );
        sym  = true;
        skip = false;
        break;
      default:
        sym = false;
    }
    if( !skip || sym ) {
      if( *ptr != ParserConsts::null ) {
        baseArgs << *ptr;
        if( wxIsalnum( *ptr ) || *ptr == ParserConsts::underscore_chr ) {
          word << *ptr;
        }
      }
    }
    if( !skip && sym ) {
      while( *ptr != ParserConsts::null && *( ptr + 1 ) == ParserConsts::space_chr ) {
        ++ptr;
      }
    }
    if( *ptr != ParserConsts::null ) {
      ++ptr;
    }
  }
  if( one && baseArgs.Len() > 2 ) {
    const wxChar ch = baseArgs[1];
    if( ( ch <= _T( '9' ) && ch >= _T( '0' ) )
        || baseArgs.Find( _T( '"' ) ) != wxNOT_FOUND
        || baseArgs.Find( _T( '\'' ) ) != wxNOT_FOUND ) {
      return false;
    }
    if( baseArgs == _T( "(void)" ) ) {
      baseArgs = _T( "()" );
    }
  }
  return true;
}

void ParserThread::GetTemplateArgs() {
  TokenizerState oldState = m_Tokenizer.GetState();
  m_Tokenizer.SetState( tsNormal );
  m_TemplateArgument.clear();
  int nestLvl = 0;
  while( IS_ALIVE ) {
    wxString tmp = m_Tokenizer.GetToken();
    if( tmp == ParserConsts::lt ) {
      ++nestLvl;
      m_TemplateArgument << tmp;
    } else if( tmp == ParserConsts::gt ) {
      --nestLvl;
      m_TemplateArgument << tmp;
    } else if( tmp == ParserConsts::semicolon ) {
      m_Tokenizer.UngetToken();
      m_TemplateArgument.clear();
      break;
    } else if( tmp.IsEmpty() ) {
      break;
    } else
    { m_TemplateArgument << tmp; }
    if( nestLvl <= 0 ) {
      break;
    }
  }
  m_Tokenizer.SetState( oldState );
}

void ParserThread::ResolveTemplateArgs( Token* newToken ) {
  newToken->m_TemplateArgument = m_TemplateArgument;
  wxArrayString actuals;
  SplitTemplateActualParameters( m_TemplateArgument, actuals );
  newToken->m_TemplateType = actuals;
  std::map<wxString, wxString> templateMap;
  ResolveTemplateMap( newToken->m_FullType, actuals, templateMap );
  newToken->m_TemplateMap = templateMap;
}

wxArrayString ParserThread::GetTemplateArgArray( const wxString& templateArgs, bool remove_gt_lt, bool add_last ) {
  wxString word;
  wxString args = templateArgs;
  args.Trim( true ).Trim( false );
  if( remove_gt_lt ) {
    args.Remove( 0, 1 );
    args.RemoveLast();
  }
  wxArrayString container;
  for( size_t i = 0; i < args.Len(); ++i ) {
    wxChar arg = args.GetChar( i );
    switch( arg ) {
      case ParserConsts::space_chr:
        container.Add( word );
        word.clear();
        continue;
      case ParserConsts::lt_chr:
      case ParserConsts::gt_chr:
      case ParserConsts::comma_chr:
        container.Add( word );
        word.clear();
        container.Add( args[i] );
        continue;
      default:
        word << args[i];
    }
  }
  if( add_last && !word.IsEmpty() ) {
    container.Add( word );
  }
  return container;
}

void ParserThread::SplitTemplateFormalParameters( const wxString& templateArgs, wxArrayString& formals ) {
  wxArrayString container = GetTemplateArgArray( templateArgs, false, false );
  size_t n = container.GetCount();
  for( size_t j = 0; j < n; ++j ) {
    if( ( container[j] == ParserConsts::kw_typename )
        || ( container[j] == ParserConsts::kw_class ) ) {
      if( ( j + 1 ) < n ) {
        formals.Add( container[j + 1] );
        ++j;
      }
    }
  }
}

void ParserThread::SplitTemplateActualParameters( const wxString& templateArgs, wxArrayString& actuals ) {
  wxArrayString container = GetTemplateArgArray( templateArgs, true, true );
  size_t n = container.GetCount();
  int level = 0;
  for( size_t j = 0; j < n; ++j ) {
    if( container[j] == ParserConsts::lt ) {
      ++level;
      while( level > 0 && ( j + 1 ) < n ) {
        if( container[j] == ParserConsts::gt ) {
          --level;
        }
        ++j;
      }
    } else if( container[j] == ParserConsts::comma ) {
      ++j;
      continue;
    } else
    { actuals.Add( container[j] ); }
    ++j;
  }
}

bool ParserThread::ResolveTemplateMap( const wxString& typeStr, const wxArrayString& actuals,
                                       std::map<wxString, wxString>& results ) {
  wxString tokenFullType = typeStr;
  TokenIdxSet fullTypeMatches;
  size_t matchesCount = m_TokenTree->FindMatches( tokenFullType, fullTypeMatches, true, false, tkTypedef );
  if( matchesCount > 0 ) {
    for( TokenIdxSet::const_iterator it = fullTypeMatches.begin(); it != fullTypeMatches.end(); ++it ) {
      int id = ( *it );
      Token* token = m_TokenTree->at( id );
      if( token->m_TokenKind == tkTypedef ) {
        tokenFullType = token->m_FullType;
        if( tokenFullType.Find( _T( "::" ) ) != wxNOT_FOUND ) {
          tokenFullType = tokenFullType.substr( tokenFullType.Find( _T( "::" ) ) + 2 );
        }
        break;
      }
    }
  }
  wxString parentType = tokenFullType;
  parentType.Trim( true ).Trim( false );
  TokenIdxSet parentResult;
  size_t tokenCounts = m_TokenTree->FindMatches( parentType, parentResult, true, false, tkClass );
  if( tokenCounts > 0 ) {
    for( TokenIdxSet::const_iterator it = parentResult.begin(); it != parentResult.end(); ++it ) {
      int id = ( *it );
      Token* normalToken = m_TokenTree->at( id );
      if( normalToken ) {
        wxArrayString formals =  normalToken->m_TemplateType;
        size_t n = formals.GetCount() < actuals.GetCount() ? formals.GetCount() : actuals.GetCount();
        for( size_t i = 0; i < n; ++i ) {
          results[formals[i]] = actuals[i];
        }
      }
    }
    return ( results.size() > 0 ) ? true : false;
  } else {
    return false;
  }
}

void ParserThread::RemoveTemplateArgs( const wxString &exp, wxString &expNoArgs, wxString &templateArgs ) {
  expNoArgs.clear();
  templateArgs.clear();
  int nestLvl = 0;
  for( unsigned int i = 0; i < exp.length(); i++ ) {
    wxChar c = exp[i];
    if( c == ParserConsts::lt_chr ) {
      nestLvl++;
      templateArgs << c;
      continue;
    }
    if( c == ParserConsts::gt_chr ) {
      nestLvl--;
      templateArgs << c;
      continue;
    }
    if( nestLvl == 0 ) {
      expNoArgs << c;
    } else {
      bool wanted = true;
      if( c == ParserConsts::space ) {
        wxChar last = 0;
        wxChar next = 0;
        if( i > 0 ) {
          last = exp[i - 1];
        }
        if( i < exp.length() - 1 ) {
          next = exp[i + 1];
        }
        if( last == ParserConsts::gt || last == ParserConsts::lt ) {
          wanted = false;
        }
        if( next == ParserConsts::gt || next == ParserConsts::lt ) {
          wanted = false;
        }
      }
      if( wanted == true ) {
        templateArgs << c;
      }
    }
  }
}

bool ParserThread::IsStillAlive( M_Code_unused const wxString& funcInfo ) {
  const bool alive = !TestDestroy();
  if( !alive ) {
    free( 0 );
  }
  return alive;
}

void ParserThread::RefineAnonymousTypeToken( short int typeMask, wxString alias ) {
  Token* unnamedAncestor = TokenExists( m_Str, m_LastParent, typeMask );
  if( unnamedAncestor && unnamedAncestor->m_IsAnonymous ) {
    if( m_Str.Contains( _T( "Union" ) ) ) {
      m_Str = _T( "union" );
    } else if( m_Str.Contains( _T( "Struct" ) ) ) {
      m_Str = _T( "struct" );
    } else
    { m_Str = _T( "tag" ); }
    m_Str << m_FileIdx << _T( "_" ) << alias;
    m_TokenTree->RenameToken( unnamedAncestor, m_Str );
  }
}

wxString ParserThread::ReadAngleBrackets() {
  wxString str = m_Tokenizer.GetToken();
  if( str != _T( "<" ) ) {
    return wxEmptyString;
  }
  int level = 1;
  while( m_Tokenizer.NotEOF() ) {
    wxString token = m_Tokenizer.GetToken();
    if( token == _T( "<" ) ) {
      ++level;
      str << token;
    } else if( token == _T( ">" ) ) {
      --level;
      str << token;
      if( level == 0 ) {
        break;
      }
    } else if( token == _T( "*" ) || token == _T( "&" ) || token == _T( "," ) ) {
      str << token;
    } else {
      if( str.Last() == _T( '<' ) ) {
        str << token;
      } else
      { str << _T( " " ) << token; }
    }
    if( level == 0 ) {
      break;
    }
  }
  return str;
}
